/* ---------------------------- dom/elementpath.js -------------------------- */
(function() {
   // Elements that are considered the "Block limit" in an element path.
   var pathBlockLimitElements = {};
   for ( var tag in VED.dtd.$blockLimit ) {
      // Exclude from list roots.
      if ( !( tag in VED.dtd.$list ) )
         pathBlockLimitElements[ tag ] = 1;
   }

   // Elements that are considered the "End level Block" in an element path.
   var pathBlockElements = {};

   for ( tag in VED.dtd.$block ) {
      // Exclude block limits, and empty block element, e.g. hr.
      if ( !( tag in VED.dtd.$blockLimit || tag in VED.dtd.$empty ) )
         pathBlockElements[ tag ] = 1;
   }

   // Check if an element contains any block element.
   var checkHasBlock = function( element ) {
      var childNodes = element.getChildren();

      for ( var i = 0, count = childNodes.count(); i < count; i++ ) {
         var child = childNodes.getItem( i );

         if ( child.type == VED.NODE_ELEMENT && VED.dtd.$block[ child.getName() ] )
            return true;
      }

      return false;
   };

   VED.dom.elementPath = new Class({
      initialize: function( startNode, root ) {
         var block = null;
         var blockLimit = null;
         var elements = [];

         // Backward compact.
         root = root || startNode.getDocument().getBody();

         var e = startNode;

         do {
            if ( e.type == VED.NODE_ELEMENT ) {
               elements.push( e );

               if ( !this.lastElement ) {
                  this.lastElement = e;

                  // If a table is fully selected at the end of the element path,
                  // it must not become the block limit.
                  if ( e.is( VED.dtd.$object ) )
                     continue;
               }

               var elementName = e.getName();

               if ( !blockLimit ) {
                  if ( !block && pathBlockElements[ elementName ] )
                     block = e;

                  if ( pathBlockLimitElements[ elementName ] ) {
                     // End level DIV is considered as the block, if no block is available. (#525)
                     // But it must NOT be as the root element.
                     if ( !block && elementName == 'div' && !checkHasBlock( e ) && !e.equals( root ) ) {
                        block = e;
                     } else
                        blockLimit = e;
                  }
               }

               if ( e.equals( root ) )
                  break;
            }
         }
         while ( ( e = e.getParent() ) );

         this.block = block;

         this.blockLimit = blockLimit;

         this.root = root;

         this.elements = elements;
      }
   });

})();

VED.dom.elementPath.implement({

   compare: function( otherPath ) {
      var thisElements = this.elements;
      var otherElements = otherPath && otherPath.elements;

      if ( !otherElements || thisElements.length != otherElements.length )
         return false;

      for ( var i = 0; i < thisElements.length; i++ ) {
         if ( !thisElements[ i ].equals( otherElements[ i ] ) )
            return false;
      }

      return true;
   },

   contains: function( query, excludeRoot, fromTop ) {
      var evaluator;
      if ( typeof query == 'string' )
         evaluator = function( node ) {
            return node.getName() == query;
         };
      if ( query instanceof VED.dom.element )
         evaluator = function( node ) {
            return node.equals( query );
         };
      else if ( VED.tools.isArray( query ) )
         evaluator = function( node ) {
            return VED.tools.indexOf( query, node.getName() ) > -1;
         };
      else if ( typeof query == 'function' )
         evaluator = query;
      else if ( typeof query == 'object' )
         evaluator = function( node ) {
            return node.getName() in query;
         };

      var elements = this.elements,
         length = elements.length;
      excludeRoot && length--;

      if ( fromTop ) {
         elements = Array.prototype.slice.call( elements, 0 );
         elements.reverse();
      }

      for ( var i = 0; i < length; i++ ) {
         if ( evaluator( elements[ i ] ) )
            return elements[ i ];
      }

      return null;
   },

   isContextFor: function( tag ) {
      var holder;

      // Check for block context.
      if ( tag in VED.dtd.$block ) {
         // Indeterminate elements which are not subjected to be splitted or surrounded must be checked first.
         var inter = this.contains( VED.dtd.$intermediate );
         holder = inter || ( this.root.equals( this.block ) && this.block ) || this.blockLimit;
         return !!holder.getDtd()[ tag ];
      }

      return true;

   },

   direction: function() {
      var directionNode = this.block || this.blockLimit || this.root;
      return directionNode.getDirection( 1 );
   }
});





